from typing import Dict, Optional, List, Union
import pandas as pd
from datetime import datetime
from enum import Enum
from dataclasses import dataclass
from ..config.config import Config

class OrderType(Enum):
    MARKET = "MARKET"
    LIMIT = "LIMIT"
    STOP = "STOP"
    STOP_LIMIT = "STOP_LIMIT"

class OrderSide(Enum):
    BUY = "BUY"
    SELL = "SELL"

class OrderStatus(Enum):
    PENDING = "PENDING"
    FILLED = "FILLED"
    PARTIALLY_FILLED = "PARTIALLY_FILLED"
    CANCELLED = "CANCELLED"
    REJECTED = "REJECTED"

@dataclass
class Order:
    symbol: str
    side: OrderSide
    order_type: OrderType
    quantity: float
    price: Optional[float] = None
    stop_price: Optional[float] = None
    status: OrderStatus = OrderStatus.PENDING
    filled_quantity: float = 0.0
    filled_price: Optional[float] = None
    timestamp: datetime = None
    id: Optional[str] = None

class OrderManager:
    def __init__(self):
        self.active_orders: Dict[str, Order] = {}
        self.filled_orders: List[Order] = []
        self.cancelled_orders: List[Order] = []
        
    def add_order(self, order: Order) -> str:
        """Add new order to active orders"""
        order.id = self._generate_order_id()
        order.timestamp = datetime.now()
        self.active_orders[order.id] = order
        return order.id
        
    def cancel_order(self, order_id: str) -> bool:
        """Cancel an active order"""
        if order_id in self.active_orders:
            order = self.active_orders.pop(order_id)
            order.status = OrderStatus.CANCELLED
            self.cancelled_orders.append(order)
            return True
        return False
        
    def get_order(self, order_id: str) -> Optional[Order]:
        """Get order by ID"""
        return self.active_orders.get(order_id)
        
    def _generate_order_id(self) -> str:
        """Generate unique order ID"""
        return f"ORDER_{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}"

class Trader:
    def __init__(self, config: Config):
        self.config = config
        self.order_manager = OrderManager()
        self.positions: Dict[str, Dict] = {}
        self.cash: float = config.trading.initial_capital
        self.trades_history: List[Dict] = []

    def execute_signal(
        self,
        symbol: str,
        signal: int,
        current_price: float,
        position_size: Optional[float] = None,
        stop_loss: Optional[float] = None
    ) -> Optional[str]:
        """
        Execute trading signal
        Args:
            signal: 1 for buy, -1 for sell, 0 for hold
            position_size: number of shares/contracts to trade
            stop_loss: stop loss price level
        """
        if signal == 0:
            return None

        side = OrderSide.BUY if signal == 1 else OrderSide.SELL
        
        # Calculate position size if not provided
        if position_size is None:
            position_size = self._calculate_position_size(current_price)

        # Create and execute order
        order = Order(
            symbol=symbol,
            side=side,
            order_type=OrderType.MARKET,
            quantity=position_size,
            price=current_price,
        )
        
        order_id = self.order_manager.add_order(order)
        self._process_order(order)
        
        # Place stop loss order if provided
        if stop_loss and side == OrderSide.BUY:
            stop_order = Order(
                symbol=symbol,
                side=OrderSide.SELL,
                order_type=OrderType.STOP,
                quantity=position_size,
                stop_price=stop_loss
            )
            self.order_manager.add_order(stop_order)
            
        return order_id

    def _process_order(self, order: Order) -> bool:
        """
        Process order execution
        """
        if order.side == OrderSide.BUY:
            cost = order.quantity * order.price
            if cost <= self.cash:
                self.cash -= cost
                self._update_position(order.symbol, order.quantity, order.price, 'buy')
                order.status = OrderStatus.FILLED
                order.filled_quantity = order.quantity
                order.filled_price = order.price
                self.trades_history.append({
                    'timestamp': order.timestamp,
                    'symbol': order.symbol,
                    'side': 'buy',
                    'quantity': order.quantity,
                    'price': order.price,
                    'cost': cost
                })
                return True
        else:  # SELL
            if order.symbol in self.positions:
                revenue = order.quantity * order.price
                self.cash += revenue
                self._update_position(order.symbol, -order.quantity, order.price, 'sell')
                order.status = OrderStatus.FILLED
                order.filled_quantity = order.quantity
                order.filled_price = order.price
                self.trades_history.append({
                    'timestamp': order.timestamp,
                    'symbol': order.symbol,
                    'side': 'sell',
                    'quantity': order.quantity,
                    'price': order.price,
                    'revenue': revenue
                })
                return True
        
        order.status = OrderStatus.REJECTED
        return False

    def _update_position(
        self,
        symbol: str,
        quantity: float,
        price: float,
        side: str
    ):
        """
        Update position after trade execution
        """
        if symbol not in self.positions:
            self.positions[symbol] = {
                'quantity': 0,
                'average_price': 0,
                'cost_basis': 0
            }
            
        position = self.positions[symbol]
        
        if side == 'buy':
            new_quantity = position['quantity'] + quantity
            new_cost = position['cost_basis'] + (quantity * price)
            position['average_price'] = new_cost / new_quantity
            position['cost_basis'] = new_cost
            position['quantity'] = new_quantity
        else:  # sell
            position['quantity'] -= quantity
            if position['quantity'] <= 0:
                del self.positions[symbol]

    def _calculate_position_size(self, current_price: float) -> float:
        """
        Calculate default position size based on available cash
        """
        return min(
            self.cash * 0.95 / current_price,  # Use 95% of available cash
            self.cash * self.config.trading.risk_per_trade / current_price
        )

    def get_portfolio_value(self, current_prices: Dict[str, float]) -> float:
        """
        Calculate total portfolio value
        """
        positions_value = sum(
            pos['quantity'] * current_prices[symbol]
            for symbol, pos in self.positions.items()
            if symbol in current_prices
        )
        return self.cash + positions_value 